package com.rizzo.android.content;

import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import com.rizzo.android.domain.Error;
import com.rizzo.android.domain.ErrorData;
import com.rizzo.android.domain.Quota;
import com.rizzo.android.domain.Trend;
import com.rizzo.android.domain.TrendData;

import java.util.Arrays;
import java.util.HashSet;

public class RizzoContentProvider extends ContentProvider {

	private static final UriMatcher sURIMatcher;
	private DatabaseHelper databaseHelper;

	private static final int ERROR = 1;
	private static final int ERROR_DATA = 2;
	private static final int QUOTA = 3;
	private static final int TREND = 4;
	private static final int TREND_DATA = 5;

	static {
		sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
		sURIMatcher.addURI(RizzoContent.AUTHORITY, Error.TABLE_NAME, ERROR);
		sURIMatcher.addURI(RizzoContent.AUTHORITY, ErrorData.TABLE_NAME, ERROR_DATA);
		sURIMatcher.addURI(RizzoContent.AUTHORITY, Quota.TABLE_NAME, QUOTA);
		sURIMatcher.addURI(RizzoContent.AUTHORITY, Trend.TABLE_NAME, TREND);
		sURIMatcher.addURI(RizzoContent.AUTHORITY, TrendData.TABLE_NAME, TREND_DATA);
	}

	@Override
	public boolean onCreate() {
		databaseHelper = DatabaseHelper.getInstance(getContext());
		return true;
	}

	@Override
	public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
		SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
		switch (sURIMatcher.match(uri)) {
			case ERROR:
				checkColumns(projection, QUOTA);
				queryBuilder.setTables(Error.TABLE_NAME);
				break;
			case ERROR_DATA:
				checkColumns(projection, QUOTA);
				queryBuilder.setTables(ErrorData.TABLE_NAME);
				break;
			case QUOTA:
				checkColumns(projection, QUOTA);
				queryBuilder.setTables(Quota.TABLE_NAME);
				break;
			case TREND:
				checkColumns(projection, TREND);
				queryBuilder.setTables(Trend.TABLE_NAME);
				break;
			case TREND_DATA:
				checkColumns(projection, TREND_DATA);
				queryBuilder.setTables(TrendData.TABLE_NAME);
				break;
			default:
				throw new RuntimeException("Unknown URI: " + uri);
		}
		SQLiteDatabase db = databaseHelper.getReadableDatabase();
		Cursor cursor = queryBuilder.query(db, projection, selection, selectionArgs, null, null, sortOrder);
		// the following makes sure that whenever a notifyChange was triggered on this uri for this cursor it will be notified
		cursor.setNotificationUri(getContext().getContentResolver(), uri);
		return cursor;
	}

	@Override
	public String getType(Uri uri) {
		switch (sURIMatcher.match(uri)) {
			case ERROR:
				return RizzoContent.Error.CONTENT_TYPE;
			case ERROR_DATA:
				return RizzoContent.ErrorData.CONTENT_TYPE;
			case QUOTA:
				return RizzoContent.Quota.CONTENT_TYPE;
			case TREND:
				return RizzoContent.Trend.CONTENT_TYPE;
			case TREND_DATA:
				return RizzoContent.TrendData.CONTENT_TYPE;
			default:
				return null;
		}
	}

	@Override
	public Uri insert(Uri uri, ContentValues initialValues) {
		ContentValues values;
		if (initialValues != null) {
			values = new ContentValues(initialValues);
		} else {
			values = new ContentValues();
		}
		SQLiteDatabase db = databaseHelper.getWritableDatabase();
		switch (sURIMatcher.match(uri)) {
			case ERROR:
				return insert(uri, values, db, Error.TABLE_NAME, Error.ID_FIELD);
			case ERROR_DATA:
				return insert(uri, values, db, ErrorData.TABLE_NAME, ErrorData.ID_FIELD);
			case QUOTA:
				return insert(uri, values, db, Quota.TABLE_NAME, Quota.ID_FIELD);
			case TREND:
				return insert(uri, values, db, Trend.TABLE_NAME, Trend.ID_FIELD);
			case TREND_DATA:
				return insert(uri, values, db, TrendData.TABLE_NAME, TrendData.ID_FIELD);
			default:
				throw new IllegalArgumentException("Unknown URI: " + uri);
		}
	}

	@Override
	public int delete(Uri uri, String selection, String[] selectionArgs) {
		SQLiteDatabase db = databaseHelper.getWritableDatabase();
		int count = 0;
		switch (sURIMatcher.match(uri)) {
			case ERROR:
				count = db.delete(Error.TABLE_NAME, selection, selectionArgs);
				break;
			case ERROR_DATA:
				count = db.delete(ErrorData.TABLE_NAME, selection, selectionArgs);
				break;
			case QUOTA:
				count = db.delete(Quota.TABLE_NAME, selection, selectionArgs);
				break;
			case TREND:
				count = db.delete(Trend.TABLE_NAME, selection, selectionArgs);
				break;
			case TREND_DATA:
				count = db.delete(TrendData.TABLE_NAME, selection, selectionArgs);
				break;
			default:
				throw new IllegalArgumentException("Unknown URI: " + uri);
		}
		// as a courtesy notify everyone that matters...
		getContext().getContentResolver().notifyChange(uri, null);
		return count;
	}

	@Override
	public int update(Uri uri, ContentValues contentValues, String selection, String[] selectionArgs) {
		SQLiteDatabase db = databaseHelper.getWritableDatabase();
		int count;
		switch (sURIMatcher.match(uri)) {
			case ERROR:
				count = db.update(Error.TABLE_NAME, contentValues, selection, selectionArgs);
				break;
			case ERROR_DATA:
				count = db.update(ErrorData.TABLE_NAME, contentValues, selection, selectionArgs);
				break;
			case QUOTA:
				count = db.update(Quota.TABLE_NAME, contentValues, selection, selectionArgs);
				break;
			case TREND:
				count = db.update(Trend.TABLE_NAME, contentValues, selection, selectionArgs);
				break;
			case TREND_DATA:
				count = db.update(TrendData.TABLE_NAME, contentValues, selection, selectionArgs);
				break;
			default:
				throw new IllegalArgumentException("Unknown URI " + uri);
		}
		// as a courtesy notify everyone that matters...
		getContext().getContentResolver().notifyChange(uri, null);
		return count;
	}

	private void checkColumns(String[] projection, int uri) {
		if (projection != null) {
			HashSet<String> requestedColumns = new HashSet<String>(Arrays.asList(projection));
			HashSet<String> availableColumns;
			switch (uri) {
				case ERROR:
					availableColumns = new HashSet<String>(Arrays.asList(Error.AVAILABLE_FIELDS));
					break;
				case ERROR_DATA:
					availableColumns = new HashSet<String>(Arrays.asList(ErrorData.AVAILABLE_FIELDS));
					break;
				case QUOTA:
					availableColumns = new HashSet<String>(Arrays.asList(Quota.AVAILABLE_FIELDS));
					break;
				case TREND:
					availableColumns = new HashSet<String>(Arrays.asList(Trend.AVAILABLE_FIELDS));
					break;
				case TREND_DATA:
					availableColumns = new HashSet<String>(Arrays.asList(TrendData.AVAILABLE_FIELDS));
					break;
				default:
					throw new IllegalArgumentException("Unknown URI: " + uri);
			}
			// Check if all columns which are requested are available
			if (!availableColumns.containsAll(requestedColumns)) {
				throw new IllegalArgumentException("Unknown columns in projection");
			}
		}
	}

	private Uri insert(Uri uri, ContentValues values, SQLiteDatabase db, String tableName, String idField) {
		long rowId = db.insert(tableName, null, values);
		if (rowId > 0) {
			Uri newUri = uri.buildUpon().appendQueryParameter(idField, String.valueOf(rowId)).build();
			getContext().getContentResolver().notifyChange(newUri, null);
			return newUri;
		} else {
			throw new SQLException("Failed to insert row into: " + uri);
		}
	}
}
